<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WitPro-YOLO训练数据可视化仪表盘</title>
    <!-- 引入 ECharts 库 -->
    <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
    <!-- CSS 样式 -->
    <style>
        /* 全局样式 */
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            font-family: 'Arial', sans-serif;
            background-color: #0a0e17;
            color: #e0e6ed;
            padding: 20px;
            overflow-x: hidden;
        }
        
        .container {
            max-width: 1400px;
            margin: 0 auto;
        }
        
        /* 标题样式 */
        .title {
            text-align: center;
            margin-bottom: 40px;
            font-size: 2.5rem;
            color: #34c759;
            text-shadow: 0 0 10px rgba(52, 199, 89, 0.5);
            letter-spacing: 2px;
        }
        
        /* 控制面板样式 */
        .control-panel {
            background-color: rgba(16, 20, 33, 0.8);
            border-radius: 10px;
            padding: 20px;
            margin-bottom: 30px;
            border: 1px solid rgba(52, 199, 89, 0.2);
            box-shadow: 0 0 20px rgba(52, 199, 89, 0.1);
            backdrop-filter: blur(5px);
        }
        
        .control-title {
            margin-bottom: 15px;
            font-size: 1.5rem;
            color: #34c759;
        }
        
        .control-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 15px;
        }
        
        .control-item {
            display: flex;
            flex-direction: column;
        }
        
        .control-label {
            margin-bottom: 5px;
            color: #7a8b9f;
        }
        
        .toggle-switch {
            position: relative;
            display: inline-block;
            width: 50px;
            height: 24px;
        }
        
        .toggle-switch input {
            opacity: 0;
            width: 0;
            height: 0;
        }
        
        .slider {
            position: absolute;
            cursor: pointer;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: #2c3e50;
            transition: .4s;
            border-radius: 24px;
        }
        
        .slider:before {
            position: absolute;
            content: "";
            height: 16px;
            width: 16px;
            left: 4px;
            bottom: 4px;
            background-color: #0a0e17;
            transition: .4s;
            border-radius: 50%;
            box-shadow: 0 0 5px rgba(52, 199, 89, 0.5);
        }
        
        input:checked + .slider {
            background-color: #34c759;
        }
        
        input:checked + .slider:before {
            transform: translateX(26px);
            background-color: #0a0e17;
        }
        
        .toggle-label {
            margin-left: 10px;
            font-size: 0.9rem;
        }
        
        /* 图表容器样式 */
        .charts-container {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(450px, 1fr));
            gap: 25px;
            margin-bottom: 30px;
        }
        
        .chart-card {
            background-color: rgba(16, 20, 33, 0.8);
            border-radius: 10px;
            padding: 20px;
            height: 400px;
            position: relative;
            overflow: hidden;
            border: 1px solid rgba(52, 199, 89, 0.2);
            box-shadow: 0 0 20px rgba(52, 199, 89, 0.1);
            transition: transform 0.3s, box-shadow 0.3s;
        }
        
        .chart-card:hover {
            transform: translateY(-5px);
            box-shadow: 0 10px 25px rgba(52, 199, 89, 0.2);
        }
        
        .chart-title {
            font-size: 1.2rem;
            margin-bottom: 15px;
            color: #34c759;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        
        .chart-info {
            font-size: 0.8rem;
            color: #7a8b9f;
            margin-bottom: 10px;
        }
        
        .chart-container {
            height: calc(100% - 70px);
        }
        
        /* 特效样式 */
        .glow-effect {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            pointer-events: none;
            overflow: hidden;
        }
        
        .glow {
            position: absolute;
            border-radius: 50%;
            background: radial-gradient(circle, rgba(52, 199, 89, 0.8) 0%, rgba(52, 199, 89, 0) 70%);
            animation: pulse 4s infinite ease-in-out;
            opacity: 0.1;
        }
        
        @keyframes pulse {
            0% {
                transform: scale(1);
                opacity: 0.1;
            }
            50% {
                transform: scale(1.2);
                opacity: 0.2;
            }
            100% {
                transform: scale(1);
                opacity: 0.1;
            }
        }
        
        /* 响应式设计 */
        @media (max-width: 768px) {
            .charts-container {
                grid-template-columns: 1fr;
            }
            
            .title {
                font-size: 1.8rem;
            }
            
            .chart-card {
                height: 350px;
            }
        }
        
        /* 加载动画 */
        .loader {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: #0a0e17;
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 10000;
            transition: opacity 0.5s;
        }
        
        .loader-content {
            text-align: center;
        }
        
        .loader-spinner {
            width: 50px;
            height: 50px;
            border: 5px solid rgba(52, 199, 89, 0.2);
            border-top: 5px solid #34c759;
            border-radius: 50%;
            margin: 0 auto 20px;
            animation: spin 1s linear infinite;
        }
        
        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
        
        .loader-text {
            color: #34c759;
            font-size: 1.2rem;
        }
    </style>
</head>
<body>
    <!-- 加载动画 -->
    <div class="loader" id="loader">
        <div class="loader-content">
            <div class="loader-spinner"></div>
            <div class="loader-text">数据加载中...</div>
        </div>
    </div>

    <div class="container">
        <h1 class="title">风机训练数据可视化仪表盘</h1>
        
        <!-- 控制面板 -->
        <div class="control-panel">
            <h2 class="control-title">图表控制面板</h2>
            <div class="control-grid" id="controlGrid">
                <!-- 控制项将通过JavaScript动态生成 -->
            </div>
        </div>
        
        <!-- 图表容器 -->
        <div class="charts-container" id="chartGrid">
            <!-- 图表将通过JavaScript动态生成 -->
        </div>
    </div>

    <!-- JavaScript -->
    <script>
        // 隐藏加载动画
        window.addEventListener('load', function() {
            setTimeout(function() {
                document.getElementById('loader').style.opacity = '0';
                setTimeout(function() {
                    document.getElementById('loader').style.display = 'none';
                }, 500);
            }, 1000);
        });

        document.addEventListener('DOMContentLoaded', function() {
            // 显示加载动画
            document.getElementById('loader').style.display = 'flex';
            
            // 图表配置
            const chartConfigs = [
                { key: 'time', title: '训练时间' },
                { key: 'train/box_loss', title: '训练框损失' },
                { key: 'train/cls_loss', title: '训练分类损失' },
                { key: 'train/dfl_loss', title: '训练DFL损失' },
                { key: 'metrics/precision(B)', title: '精准率 (B)' },
                { key: 'metrics/recall(B)', title: '召回率 (B)' },
                { key: 'metrics/mAP50(B)', title: 'mAP@50 (B)' },
                { key: 'metrics/mAP50-95(B)', title: 'mAP@50-95 (B)' },
                { key: 'val/box_loss', title: '验证框损失' },
                { key: 'val/cls_loss', title: '验证分类损失' },
                { key: 'val/dfl_loss', title: '验证DFL损失' },
                { key: 'lr/pg0', title: '学习率 - 组0' },
                { key: 'lr/pg1', title: '学习率 - 组1' },
                { key: 'lr/pg2', title: '学习率 - 组2' }
            ];
            
            // 从本地JSON文件加载数据
            fetch('./output.json')
                .then(response => {
                    if (!response.ok) {
                        throw new Error(`HTTP error! status: ${response.status}`);
                    }
                    return response.json();
                })
                .then(data => {
                    // 隐藏加载动画
                    document.getElementById('loader').style.display = 'none';
                    
                    // 创建图表容器和控制项
                    const gridContainer = document.getElementById('chartGrid');
                    const controlGrid = document.getElementById('controlGrid');
                    
                    const charts = {};
                    
                    chartConfigs.forEach((config, index) => {
                        // 创建图表卡片
                        const card = document.createElement('div');
                        card.className = 'chart-card';
                        card.id = `chart-card${index + 1}`;
                        
                        const chartTitle = config.title;
                        
                        card.innerHTML = `
                            <div class="chart-title">
                                ${chartTitle}
                                <span class="chart-info">${config.key}</span>
                            </div>
                            <div class="chart-container" id="${config.key}"></div>
                        `;
                        gridContainer.appendChild(card);
                        
                        // 创建控制项
                        const controlItem = document.createElement('div');
                        controlItem.className = 'control-item';
                        controlItem.innerHTML = `
                            <label class="control-label">${chartTitle}</label>
                            <label class="toggle-switch">
                                <input type="checkbox" id="toggle-chart${index + 1}" checked>
                                <span class="slider"></span>
                            </label>
                            <span class="toggle-label">显示/隐藏</span>
                        `;
                        controlGrid.appendChild(controlItem);
                        
                        // 创建图表实例
                        const chartDom = document.getElementById(config.key);
                        if (chartDom) {
                            charts[config.key] = echarts.init(chartDom);
                            
                            // 准备数据
                            // 提取当前指标的所有epoch数据
                            const values = [];
                            const epochs = [];
                            
                            // 遍历所有epoch
                            for (const epoch in data[config.key]) {
                                if (data[config.key].hasOwnProperty(epoch)) {
                                    epochs.push(Number(epoch));
                                    values.push(data[config.key][epoch]);
                                }
                            }
                            
                            // 按epoch排序
                            const sortedIndices = epochs.map((_, index) => index).sort((a, b) => epochs[a] - epochs[b]);
                            const sortedEpochs = sortedIndices.map(i => epochs[i]);
                            const sortedValues = sortedIndices.map(i => values[i]);
                            
                            // 配置图表选项
                            const option = {
                                tooltip: {
                                    trigger: 'axis',
                                    formatter: function(params) {
                                        return `${params[0].name} epoch<br/>${chartTitle}: ${params[0].value.toFixed(4)}`;
                                    }
                                },
                                grid: {
                                    left: '3%',
                                    right: '13%',
                                    bottom: '3%',
                                    containLabel: true
                                },
                                xAxis: {
                                    type: 'category',
                                    name: 'epoch',
                                    nameTextStyle: {
                                        color: '#a0aec0'
                                    },
                                    data: sortedEpochs,
                                    axisLine: {
                                        lineStyle: {
                                            color: '#4a5568'
                                        }
                                    },
                                    axisLabel: {
                                        color: '#a0aec0'
                                    },
                                    splitLine: {
                                        show: true,
                                        lineStyle: {
                                            color: 'rgba(74, 85, 104, 0.3)'
                                        }
                                    }
                                },
                                yAxis: {
                                    type: 'value',
                                    name: config.key.includes('loss') ? '损失值' : 
                                           config.key.includes('precision') || config.key.includes('recall') ? '率' : 
                                           config.key.includes('mAP') ? 'AP值' :
                                           config.key.includes('lr') ? '学习率' : '值',
                                    nameTextStyle: {
                                        color: '#a0aec0'
                                    },
                                    axisLine: {
                                        lineStyle: {
                                            color: '#4a5568'
                                        }
                                    },
                                    axisLabel: {
                                        color: '#a0aec0'
                                    },
                                    splitLine: {
                                        lineStyle: {
                                            color: 'rgba(74, 85, 104, 0.3)'
                                        }
                                    }
                                },
                                series: [{
                                    data: sortedValues,
                                    type: 'line',
                                    smooth: true,
                                    symbol: 'circle',
                                    symbolSize: 6,
                                    itemStyle: {
                                        color: function(params) {
                                            const colors = ['#34c759', '#3498db', '#e74c3c', '#f39c12', '#9b59b6', '#1abc9c'];
                                            return colors[params.dataIndex % colors.length];
                                        }
                                    },
                                    lineStyle: {
                                        width: 2,
                                        shadowColor: function(params) {
                                            const colors = ['rgba(52, 199, 89, 0.3)', 'rgba(52, 152, 219, 0.3)', 'rgba(231, 76, 60, 0.3)', 
                                                          'rgba(243, 156, 18, 0.3)', 'rgba(155, 89, 182, 0.3)', 'rgba(26, 188, 156, 0.3)'];
                                            return colors[params.dataIndex % colors.length];
                                        },
                                        shadowBlur: 10
                                    },
                                    areaStyle: {
                                        color: {
                                            type: 'linear',
                                            x: 0,
                                            y: 0,
                                            x2: 0,
                                            y2: 1,
                                            colorStops: [{
                                                offset: 0,
                                                color: 'rgba(52, 199, 89, 0.3)'
                                            }, {
                                                offset: 1,
                                                color: 'rgba(52, 199, 89, 0.05)'
                                            }]
                                        }
                                    }
                                }],
                                backgroundColor: 'transparent'
                            };
                            
                            // 对学习率的 Y 轴进行特殊处理
                            if (config.key.includes('lr')) {
                                option.series[0].label = {
                                    formatter: function(value) {
                                        return value < 1e-4 ? value.toExponential(2) : value.toFixed(6);
                                    }
                                };
                                option.yAxis.axisLabel.formatter = function(value) {
                                    return value < 1e-4 ? value.toExponential(2) : value.toFixed(6);
                                };
                            }
                            
                            // 应用图表配置
                            charts[config.key].setOption(option);
                        }
                    });
                    
                    // 添加开关切换事件监听
                    chartConfigs.forEach((config, index) => {
                        const toggle = document.getElementById(`toggle-chart${index + 1}`);
                        if (toggle) {
                            toggle.addEventListener('change', function() {
                                if (this.checked) {
                                    if (charts[config.key]) {
                                        document.getElementById(`chart-card${index + 1}`).style.display = 'block';
                                        charts[config.key].resize();
                                        charts[config.key].setOption(charts[config.key].getOption());
                                    }
                                } else {
                                    document.getElementById(`chart-card${index + 1}`).style.display = 'none';
                                }
                            });
                        }
                    });
                    
                    // 窗口大小调整时重新调整图表大小
                    window.addEventListener('resize', function() {
                        for (const key in charts) {
                            if (charts[key]) {
                                charts[key].resize();
                            }
                        }
                    });
                    
                    // 添加图表卡片的动画效果
                    const chartCards = document.querySelectorAll('.chart-card');
                    chartCards.forEach(card => {
                        card.addEventListener('mouseenter', function() {
                            const glow = document.createElement('div');
                            glow.className = 'glow';
                            glow.style.width = '200%';
                            glow.style.height = '200%';
                            glow.style.left = '-50%';
                            glow.style.top = '-50%';
                            this.querySelector('.glow-effect')?.remove();
                            const glowEffect = document.createElement('div');
                            glowEffect.className = 'glow-effect';
                            glowEffect.appendChild(glow);
                            this.appendChild(glowEffect);
                        });

                        card.addEventListener('mouseleave', function() {
                            this.querySelector('.glow-effect')?.remove();
                        });
                    });
                })
                .catch(error => {
                    console.error('加载JSON文件时出错:', error);
                    document.getElementById('loader').style.display = 'none';
                    alert('加载数据失败，请检查output.json文件是否存在且格式正确');
                });
        });
    </script>
</body>
</html>